package com.acegear.horizon.domain.models;

import com.acegear.horizon.ApplicationContextProvider;
import com.acegear.horizon.domain.events.PostEventType;
import com.acegear.horizon.domain.events.UserPostDeleteEvent;
import com.acegear.horizon.domain.events.processers.EventProcessor;
import com.acegear.horizon.domain.models.constraint.ReportType;
import com.acegear.horizon.domain.models.constraint.UserPostType;
import com.acegear.horizon.domain.models.jpa.*;
import com.acegear.horizon.domain.models.vo.CommentVO;
import com.acegear.horizon.domain.models.vo.UserPostVO;
import com.acegear.horizon.domain.repository.CommentRepository;
import com.acegear.horizon.domain.repository.UserRepository;
import com.acegear.horizon.domain.repository.jpa.ClubBaseRepository;
import com.acegear.horizon.domain.repository.jpa.PostLikeRepository;
import com.acegear.horizon.domain.repository.jpa.ReportRepository;
import com.acegear.horizon.domain.repository.jpa.UserPostRepository;
import com.acegear.horizon.domain.services.ContentPartService;
import com.acegear.horizon.domain.services.PictureService;
import org.springframework.data.domain.Pageable;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Created by wangsike on 2016/12/23.
 */
public class Post {

    private UserPost userPost;

    private UserPostRepository userPostRepository;

    private PostLikeRepository postLikeRepository;

    private UserRepository userRepository;

    private ClubBaseRepository clubBaseRepository;

    private CommentRepository commentRepository;

    private ReportRepository reportRepository;

    private ContentPartService contentPartService;

    private ModelFactory modelFactory;

    private EventProcessor eventProcessor;

    private PictureService pictureService;


    public Post(UserPost userPost) {
        init();
        this.userPost = userPost;
    }

    public Post(Long clubId, Long userId, Integer type, List<ContentPart> contentParts) {
        init();
        Long postId = modelFactory.nextPostId();
        userPost = new UserPost(postId, clubId, userId, UserPostType.getPostType(type), contentParts, false);
    }

    public UserPost getUserPost() {
        return userPost;
    }

    public void setUserPost(UserPost userPost) {
        this.userPost = userPost;
    }

    public UserPostVO getUserPostVO(Long viewerId) {
        User user = userRepository.findByUserId(userPost.getUserId()).get();
        UserInfo userInfo = user.getUserInfo();
        Integer likeCount = getLikeCount();
        ClubBase clubBase = clubBaseRepository.findByClubId(userPost.getClubId()).get();
        Integer commentCount = getCommentCount();
        boolean isLike = isLike(viewerId);

        return new UserPostVO(userPost.getPostId(),
                userPost.getClubId(),
                isLike,
                clubBase.getClubName(),
                userInfo.getNickname(),
                userInfo.getAvatar(),
                userInfo.getUserId(),
                likeCount,
                commentCount,
                userPost.getPostType(),
                userPost.getContents(),
                userPost.getCreateAt(),
                userPost.isDeleted());
    }

    public Integer getLikeCount() {
        return postLikeRepository.countByPostId(getPostId());
    }

    public Integer getCommentCount() {
        return commentRepository.countByPostId(getPostId());
    }

    public Boolean isLike(Long viewerId) {
        return postLikeRepository.findTopByUserIdAndPostId(viewerId, getPostId()).isPresent();
    }

    public void init() {
        userPostRepository = ApplicationContextProvider.getBean(UserPostRepository.class);

        postLikeRepository = ApplicationContextProvider.getBean(PostLikeRepository.class);

        modelFactory = ApplicationContextProvider.getBean(ModelFactory.class);

        userRepository = ApplicationContextProvider.getBean(UserRepository.class);

        clubBaseRepository = ApplicationContextProvider.getBean(ClubBaseRepository.class);

        reportRepository = ApplicationContextProvider.getBean(ReportRepository.class);

        contentPartService = ApplicationContextProvider.getBean(ContentPartService.class);

        commentRepository = ApplicationContextProvider.getBean(CommentRepository.class);

        eventProcessor = ApplicationContextProvider.getBean(EventProcessor.class);
        pictureService = ApplicationContextProvider.getBean(PictureService.class);
    }

    public Long getPostId() {
        return userPost.getPostId();
    }

    public Post save() {
        contentPartService.format(userPost.getContents());
        userPost = userPostRepository.save(userPost);
        return this;
    }

    public Boolean like(Long userId) {
        if (postLikeRepository.findTopByUserIdAndPostId(userId, getPostId())
                .isPresent()) {
            return false;
        } else {
            postLikeRepository.save(new PostLike(getPostId(), userId));
            return true;
        }
    }

    public Boolean deletePost(String rank) {
        userPost.setDeleted(true);
        userPost.setRank(rank);
        userPost.getContents().forEach((contents -> {
            switch (contents.getType()) {
                case IMAGE:
                    contents.setContent(pictureService.clientImage);
                    break;
                case VIDEO:
                    contents.setContent(pictureService.clientImage);
                    break;
                case TEXT:
                    contents.setContent("该动态已被楼主删除");
                    break;
            }
        }));
        save();
        publishPostDelete(PostEventType.CLIENT);
        return true;
    }

    public Boolean deleteComment(Long commentId, Long userId) {
        return commentRepository
                .findCommentId(commentId)
                .filter(comment -> comment.getPostComment().getUserId().equals(userId))
                .filter(Comment::deleteComment)
                .map(result -> true)
                .orElse(false);
    }

    public void deleteManagePost(String reason) {
        userPost.setDeleted(true);
        userPost.getContents().forEach((contents -> {
            switch (contents.getType()) {
                case TITLE:
                    break;
                case TEXT:
                    contents.setContent(reason);
                    break;
                case SUMMARY:
                    contents.setContent(reason);
                    break;
                case COVER:
                    contents.setContent(pictureService.managementImage);
                    break;
                case IMAGE:
                    contents.setContent(pictureService.managementImage);
                    break;
                case VIDEO:
                    contents.setContent(pictureService.managementImage);
                    break;
            }
        }));
        save();
    }

    public boolean unlike(Long userId) {
        postLikeRepository
                .deleteByPostIdAndUserId(getPostId(), userId);
        return true;
    }

    private Long getFloorCount() {
        return commentRepository.getCurrentFloor(getPostId());
    }

    public Optional<CommentVO> comment(Long userId, Long replyTo, List<ContentPart> contentParts) {
        try {
            Long replyFloor;
            replyTo = replyTo == null ? 0 : replyTo;
            replyFloor = commentRepository
                    .findCommentId(replyTo)
                    .map(Comment::getFloor)
                    .orElse(0L);
            Comment comment = new Comment(getPostId(), userId, getFloorCount() + 1, replyTo, replyFloor, contentParts);

            comment.save();

            comment.publishPostCommentCreate(PostEventType.CLIENT);

            return Optional.of(comment.getCommentVO());
        } catch (Throwable t) {
            t.printStackTrace();
            return Optional.empty();
        }
    }

    public List<CommentVO> getComments(Pageable pageable) {
        return commentRepository
                .findByPostId(getPostId(), pageable)
                .map(Comment::getCommentVO)
                .collect(Collectors.toList());
    }

    public CommentVO getComments(Long commentId) {
        return commentRepository
                .findCommentId(commentId).map(Comment::getCommentVO).orElse(null);
    }

    public Report report(Long userId) {
        Report report = new Report(ReportType.POST, userId, getPostId());
        return reportRepository.save(report);
    }

    private void publishPostDelete(PostEventType postEventType) {
        eventProcessor.publish(new UserPostDeleteEvent(getPostId(), null, postEventType));
    }

}
